﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BitmapTagger
{
	/// <summary>
	/// Represents a signed rational number used in tags, 
	/// a signed rational is one int divided by another int
	/// </summary>
	public class Rational
	{
		private int numerator;
		private int denominator;


		#region constructors

		/// <summary>
		/// Create a new signed Rational
		/// </summary>
		/// <param name="encodedRational">A Rational encoded as a Int64, numerator as the low bits, denominator as the high bits</param>
		public Rational(Int64 encodedRational)
		{
			Int64 converted = (Int64)((UInt64)encodedRational & 0xFFFFFFFF00000000);

			denominator = (int)(converted >> 32);

			converted = encodedRational & 0x00000000FFFFFFFF;

			numerator = (int)converted;
		}

		/// <summary>
		/// Create a new signed rational
		/// </summary>
		/// <param name="numerator">The numerator (top number in division)</param>
		/// <param name="denominator">The denominator (bottom number in division)</param>
		public Rational(int numerator, int denominator)
		{
			this.numerator = numerator;
			this.denominator = denominator;
		}

		/// <summary>
		/// Create a new signed rational from a string, see example for string formats
		/// </summary>
		/// <param name="rational">The rational in string format</param>
		/// <example>
		/// 78
		/// 1/600
		/// </example>
		public Rational(string rational)
		{
			if (int.TryParse(rational, out numerator))
			{
				denominator = 1;
			}
			else if (rational.Contains('/'))
			{
				char[] splitChar = new char[1] { '/' };
				string[] strings = rational.Split(splitChar, 2);

				if(!int.TryParse(strings[0], out numerator))
					throw new FormatException("Rational string not in proper format");
				if(!int.TryParse(strings[1], out denominator))
					throw new FormatException("Rational string not in proper format");
				
			}
			else
			{
				throw new FormatException("Rational string not in proper format");
			}
		}

		#endregion

		/// <summary>
		/// Get the numerator
		/// </summary>
		public int Numerator { get { return numerator; } }

		/// <summary>
		/// Get the denominator
		/// </summary>
		public int Denominator { get { return denominator; } }

		#region conversions and castings

		/// <summary>
		/// Get a string that represents the Rational
		/// </summary>
		/// <returns>Arepresents the rational </returns>
		public override string ToString()
		{
			return String.Format("{0}/{1}", Numerator, Denominator);
		}

		/// <summary>
		/// Explicit type casting from Rational to string
		/// </summary>
		/// <param name="rational">The Rational to cast as a string</param>
		/// <returns>The Rational as a string</returns>
		public static explicit operator string(Rational rational)
		{
			return rational.ToString();
		}

		/// <summary>
		/// Get the rational as a float (numerator / denominator)
		/// </summary>
		/// <returns>A float value representing the Rational</returns>
		public float ToFloat()
		{
			return (float)Numerator / (float)Denominator;
		}

		/// <summary>
		/// Explicit type casting from Rational to float
		/// </summary>
		/// <param name="rational">The Rational to cast as a float</param>
		/// <returns>The Rational as a float</returns>
		public static explicit operator float(Rational rational)
		{
			return rational.ToFloat();
		}

		/// <summary>
		/// Get the Rational encoded as an Int64 (numerator as the low bits, denominator as high bits)
		/// </summary>
		/// <returns>The Rational encoded as an Int64</returns>
		public Int64 ToInt64()
		{
			Int64 numeratorConverted = (Int64)Numerator;
			Int64 denominatorConverted = (Int64)Denominator;
			numeratorConverted = numeratorConverted & 0x00000000FFFFFFFF;
			denominatorConverted = denominatorConverted & 0x00000000FFFFFFFF;
			denominatorConverted = denominatorConverted << 32;

			return denominatorConverted | numeratorConverted;

		}

		/// <summary>
		/// Explicit type casting from Rational to Int64, encoded with numerator as the low bits, 
		/// denominator as the high bits
		/// </summary>
		/// <param name="rational">The Rational to cast as an Int64</param>
		/// <returns>The Rational as an Int64</returns>
		public static explicit operator Int64(Rational rational)
		{
			return rational.ToInt64();
		}

		#endregion

	}
}
